/*
  Copyright 2006 by Sean Luke and George Mason University
  Licensed under the Academic Free License version 3.0
  See the file "LICENSE" for more information
*/

package sim.portrayal.inspector;
import java.awt.*;
import java.awt.event.*;
import sim.util.*;
import java.io.*;
import sim.display.*;
import sim.engine.*;
import javax.swing.*;
import sim.util.gui.*;

/** A PropertyInspector which streams its result out to a file, window, or stream. */

public class StreamingPropertyInspector extends PropertyInspector
    {
    PrintWriter out;
    boolean shouldCloseOnStop = true;
    int streamingTo;
    int interval = 1;
    static final int CUSTOM = -1;
    static final int FILE = 0;
    static final int WINDOW = 1;
    static final int STDOUT = 2;
    JTextArea area;
    JScrollPane pane;
    JFrame frame;
    double lastTime  = Schedule.BEFORE_SIMULATION;
    
    public static String name() { return "Stream"; }
    public static Class[] types() { return null; } // accepts all types
        
    /** Creates a custom StreamingPropertyInspector which writes to the provided stream, with the associated short name streamName.
        This constructor is intended for people who want to create custom streaming inspectors programmatically. */
    public StreamingPropertyInspector(Properties properties, int index, 
        Frame parent, GUIState simulation, PrintWriter stream, String streamName)
        {
        super(properties,index,parent,simulation);
        out = stream;
        setLayout(new BorderLayout());
        add(new JLabel("Streaming to..."), BorderLayout.NORTH);
        add(new JLabel(streamName), BorderLayout.CENTER);
        streamingTo = CUSTOM;
        setValidInspector(true);
        }
        
    public StreamingPropertyInspector(final Properties properties, final int index, final Frame parent, final GUIState simulation)
        {
        super(properties,index,parent,simulation);
                
        Object[] possibilities = {"A file (overwriting)",
                                  "A file (appending)",
                                  "A window", 
                                  "Standard Out"};
        String s = (String)JOptionPane.showInputDialog(
            parent,
            "Stream the property to:",
            "Stream",
            JOptionPane.PLAIN_MESSAGE,
            null,
            possibilities,
            possibilities[0]);
                                        
        if (s!=null)  // valid inspector maybe?
            {
            // make the interval (skip) field
            NumberTextField skipField = new NumberTextField("Skip: ", 1, false)
                {
                public double newValue(double newValue)
                    {
                    int val = (int) newValue;
                    if (val < 1) val = (int)currentValue;
                    interval = val;
                    return val;
                    }
                };
            skipField.setToolTipText("Specify the number of steps between stream fetches");
            skipField.setBorder(BorderFactory.createEmptyBorder(2,2,0,2));
                        
            if (s.equals(possibilities[0]) || s.equals(possibilities[1]))
                {
                streamingTo = FILE;
                                
                FileDialog fd = new FileDialog(parent,"Stream the Property " + 
                    (s.equals(possibilities[1]) ? "(appending) " : "") + 
                    "\"" + properties.getName(index) + "\" to File...", FileDialog.SAVE);
                fd.setFile(properties.getName(index)+".out");
                fd.setVisible(true);
                if (fd.getFile()!=null) try
                                            {
                                            File file = new File(fd.getDirectory(), Utilities.ensureFileEndsWith(fd.getFile(),".out"));
                                            // we'll make a writer that appends or doesn't append -- note we use
                                            // new FileWriter(String,appends) because new FileWriter(file,appends) 
                                            // is only available in Java 1.4 and up.
                                            out = new PrintWriter(new BufferedWriter(new FileWriter(file.getCanonicalPath(), s.equals(possibilities[1]))));
                                            setLayout(new BorderLayout());
                                            Box b = new Box(BoxLayout.Y_AXIS);
                                            b.add(skipField);
                                            b.add(new JLabel("Streaming to" + 
                                                    (s.equals(possibilities[1]) ? " (appending)" : "") + 
                                                    "..."));
                                            b.add(new JLabel(file.getPath()));
                                            b.add(new JLabel("Format: \"timestamp: value\""));
                                            b.add(Box.createGlue());
                                            add(b,BorderLayout.NORTH);
                                            setValidInspector(true);
                                            }
                    catch (IOException e)
                        {
                        e.printStackTrace();
                        }
                }
            else if (s.equals(possibilities[2]))
                {
                streamingTo = WINDOW;
                area = new JTextArea();
                pane = new JScrollPane(area);
                setLayout(new BorderLayout());
                add(pane,BorderLayout.CENTER);
                add(skipField, BorderLayout.NORTH);
                Box box = new Box(BoxLayout.X_AXIS);
                JButton saveButton = new JButton("Save Contents");
                box.add(saveButton);
                saveButton.addActionListener(new ActionListener()
                    {
                    public void actionPerformed(ActionEvent e)
                        {
                        synchronized(simulation.state.schedule)  // stop the simulation for a sec
                            {
                            FileDialog fd = new FileDialog(frame,"Save the (Present) Contents to File...", FileDialog.SAVE);
                            fd.setFile(properties.getName(index)+".out");
                            fd.setVisible(true);
                            if (fd.getFile()!=null) try
                                                        {
                                                        File file = new File(fd.getDirectory(), Utilities.ensureFileEndsWith(fd.getFile(),".out"));
                                                        // we'll make a writer that appends or doesn't append -- note we use
                                                        // new FileWriter(String,appends) because new FileWriter(file,appends) 
                                                        // is only available in Java 1.4 and up.
                                                        PrintWriter p = new PrintWriter(new BufferedWriter(new FileWriter(file)));
                                                        p.println(area.getText());
                                                        p.close();
                                                        }
                                catch (IOException ex)
                                    {
                                    ex.printStackTrace();
                                    }
                            }
                        }                                       
                    });
                box.add(new JLabel("Format: \"timestamp: value\""));
                box.add(Box.createGlue());
                add(box, BorderLayout.SOUTH);
                setValidInspector(true);
                }
            else // s.equals(possibilities[3])
                {
                streamingTo = STDOUT;
                shouldCloseOnStop = false;  // don't want to close System.out!
                setLayout(new BorderLayout());
                Box b = new Box(BoxLayout.Y_AXIS);
                b.add(skipField);
                b.add(new JLabel("Streaming to Standard Out"),BorderLayout.CENTER);
                b.add(new JLabel("Format: \"timestamp/object/property: value\""));
                b.add(Box.createGlue());
                add(b,BorderLayout.NORTH);
                setValidInspector(true);
                }
            }
        }
                
    public void updateInspector()
        {
        double time = simulation.state.schedule.getTime();
        if (time >= Schedule.EPOCH && time < Schedule.AFTER_SIMULATION &&
            lastTime <= time - interval)
            {
            lastTime = time;
            switch(streamingTo)
                {
                case FILE: case CUSTOM:
                    if (out != null) out.println(time + ": " + properties.getValue(index));
                    break;
                case WINDOW:
                    area.append(time + ": " + properties.getValue(index) + "\n");
                    break;
                case STDOUT:
                    System.out.println(properties.getObject() + "/" + properties.getName(index) + 
                        "/" + time + ": " + properties.getValue(index));
                    break;
                }
            }
        }
    
    public Stoppable reviseStopper(Stoppable stopper)
        {
        // we want to flush our streams if we've been stopped
        final Stoppable newStopper = super.reviseStopper(stopper);
        return new Stoppable()
            {
            public void stop()
                {
                if (newStopper != null) newStopper.stop();
                if (out!=null)
                    {
                    if (streamingTo == STDOUT) out.flush();
                    else if (streamingTo == FILE || streamingTo == CUSTOM) out.close();
                    }
                out = null;  // so we don't write to it any more
                }
            };
        }
        
    public JFrame createFrame(final Stoppable stopper)
        {
        frame = super.createFrame(stopper);
        frame.getContentPane().setLayout(new BorderLayout());  // just in case
        frame.getContentPane().removeAll();  // get rid of the built-in scroller
        frame.getContentPane().add(this, BorderLayout.CENTER);
        if (pane!=null) frame.setSize(400,300);
        else frame.pack();
        return frame;
        }
    }
